package cn.demoncat.util.exec;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;

import cn.demoncat.util.exception.BizErrorRuntimeException;

/**
 * SFTP服务器操作工具 -- 直接通过SSH操作服务器文件
 * 
 * @author 延晓磊
 *
 * @since 2018年3月23日
 */
public class SftpUtil {

	/**
	 * 上传文件
	 * 
	 * @param client
	 * @param dir		文件的目录
	 * @param fileName	上传的文件
	 * @param file		文件流
	 * 
	 * @return dir + fileName
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年3月23日
	 */
	public static String upload(SftpClient client, String dir, String fileName, InputStream file) {
		// 获取连接
		ChannelSftp sftp = client.getChannelSftp();
		// 上传目录和文件名
		try {
			// 检查目录
			checkDir(sftp, dir);
			// 切换目录
			sftp.cd(dir);
			// 上传文件
			sftp.put(file, fileName);
			return dir + fileName;
		} catch (SftpException e) {
			throw new BizErrorRuntimeException("上传文件到SFTP失败：file = "+ dir + fileName, e);
		} finally {
			// 防止网络连接不稳定，每次操作都断开并新建连接，而不复用连接。如果要执行批量操作，可以单独编写
			disConnect(client);
		}
	}

	/**
	 * 下载文件
	 * 
	 * @param client
	 * @param dir		文件的目录
	 * @param fileName	下载的文件
	 * @param out		输出流
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年3月23日
	 */
	public static void download(SftpClient client, String dir, String fileName, OutputStream out) {
		// 获取连接
		ChannelSftp sftp = client.getChannelSftp();
		try {
			// 切换目录
			sftp.cd(dir);
			// 下载文件
			sftp.get(fileName, out);
		} catch (SftpException e) {
			throw new BizErrorRuntimeException("从SFTP下载文件失败：file = "+ dir + fileName, e);
		} finally {
			// 断开连接
			disConnect(client);
		}
	}

	/**
	 * 删除文件
	 * 
	 * @param client
	 * @param dir		文件的目录
	 * @param fileName	文件名称
	 *        
	 */
	public static void delete(SftpClient client, String dir, String fileName) {
		// 获取连接
		ChannelSftp sftp = client.getChannelSftp();
		try {
			// 切换目录
			sftp.cd(dir);
			// 删除文件
			sftp.rm(fileName);
		} catch (SftpException e) {
			throw new BizErrorRuntimeException("从SFTP删除文件失败：file = " + dir + fileName, e);
		} finally {
			// 断开连接
			disConnect(client);
		}
	}

	/**
	 * 连接sftp服务器
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年3月23日
	 */
	public static SftpClient connect(String username, String password, String ip, int port) {
		SftpClient client = new SftpClient();
		try {
			// 创建SSH连接
			JSch jsch = new JSch();
			Session sshSession = jsch.getSession(username, ip, port);
			sshSession.setPassword(password);
			Properties sshConfig = new Properties();
			sshConfig.put("userauth.gssapi-with-mic", "no");
			sshConfig.put("StrictHostKeyChecking", "no");
			sshSession.setConfig(sshConfig);
			sshSession.connect();
			// 创建SFTP通道
			ChannelSftp channelSftp = (ChannelSftp) sshSession.openChannel("sftp");
			channelSftp.connect();
			// 整合
			client.setChannelSftp(channelSftp);
			client.setSshSession(sshSession);
			return client;
		} catch (Exception e) {
			// 连接失败，安全断开
			disConnect(client);
			throw new BizErrorRuntimeException("连接SFTP系统失败：ip = " + ip + " , username = " + username, e);
		}
	}

	/**
	 * 断开连接
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年3月23日
	 */
	public static void disConnect(SftpClient client) {
		if (client.getChannelSftp() != null) {
			client.getChannelSftp().disconnect();
		}
		if (client.getSshSession() != null) {
			client.getSshSession().disconnect();
		}
	}

	/**
	 * 客户端连接
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年4月19日
	 */
	public static class SftpClient {
		// SSH连接
		private Session sshSession;
		// SFTP通道
		private ChannelSftp channelSftp;

		public Session getSshSession() {
			return sshSession;
		}

		public void setSshSession(Session sshSession) {
			this.sshSession = sshSession;
		}

		public ChannelSftp getChannelSftp() {
			return channelSftp;
		}

		public void setChannelSftp(ChannelSftp channelSftp) {
			this.channelSftp = channelSftp;
		}
	}
	
	/**
	 * 检查目录，不存在则创建
	 * 
	 * @param sftp
	 * @param dir
	 * 
	 * @author 延晓磊
	 * @throws SftpException 
	 *
	 * @since 2019年8月20日
	 */
	private static void checkDir(ChannelSftp sftp, String dir) throws SftpException {
		// 判断目录是否存在
		try {
			// 列出目录下的文件
			sftp.ls(dir);
		} catch (Exception e) {
			// ls失败，表示目录未找到，于是创建目录
			sftp.mkdir(dir);
		}
	}
}
